package com.zlyx.easy.swagger.web;

import static com.google.common.base.Strings.isNullOrEmpty;
import static springfox.documentation.swagger.common.HostNameProvider.componentsFrom;

import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.codehaus.plexus.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.UriComponents;

import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.zlyx.easy.core.utils.HttpUtils.HttpResponse;
import com.zlyx.easy.core.utils.RequestUtils;
import com.zlyx.easy.swagger.cache.ApiCache;
import com.zlyx.easy.swagger.config.EasySwaggerConfiguration;
import com.zlyx.easy.swagger.remote.ApiManagerService;

import io.swagger.annotations.Api;
import io.swagger.models.Swagger;
import springfox.documentation.service.Documentation;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.spring.web.json.Json;
import springfox.documentation.spring.web.json.JsonSerializer;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;

/**
 * @Auth 赵光
 * @Describle
 */
@Api(hidden = true)
@Controller
@RequestMapping("${springfox.documentation.swagger.v2.path:/}")
public class EasySwaggerController {

	private static final Logger logger = LoggerFactory.getLogger(EasySwaggerController.class);
	private final String hostNameOverride;
	private final DocumentationCache documentationCache;
	private final ServiceModelToSwagger2Mapper mapper;
	private final JsonSerializer jsonSerializer;

	@Value("${springfox.documentation.swagger.v2.path:/}")
	private String webServer;

	@Autowired
	private EasySwaggerConfiguration apiConfiguration;

	@Autowired
	private ApiManagerService apiManagerService;

	@Autowired
	public EasySwaggerController(Environment environment, DocumentationCache documentationCache,
			ServiceModelToSwagger2Mapper mapper, JsonSerializer jsonSerializer) {

		this.hostNameOverride = environment.getProperty("springfox.documentation.swagger.v2.host", "DEFAULT");
		this.documentationCache = documentationCache;
		this.mapper = mapper;
		this.jsonSerializer = jsonSerializer;
	}

	@GetMapping("/easy-swagger")
	public String swagger(String groupName) {
		if (!webServer.endsWith("/")) {
			webServer += "/";
		}
		String url = "redirect:/docs.html?webServer=" + webServer;
		if (StringUtils.isNotEmpty(groupName)) {
			url += "groupName=" + groupName;
		}
		return url;
	}

	@ResponseBody
	@RequestMapping("/easy-swagger/apis/register")
	public HttpResponse registerApis(@RequestBody Map<String, List<String>> apis) {
		ApiCache.addApis(apis);
		return HttpResponse.success();
	}

	@ResponseBody
	@RequestMapping("/easy-swagger/apis/get")
	public Map<String, List<String>> getGroupNames() {
		try {
			Map<String, List<String>> groupNames = null;
			if (apiConfiguration.getManagerUrl() != null
					&& !RequestUtils.getHost().equals(apiConfiguration.getManagerUrl())) {
				groupNames = apiManagerService.getApis();
			}
			if (groupNames == null && (apiConfiguration.getManagerUrl() == null
					|| (apiConfiguration.getManagerUrl() != null && apiConfiguration.isAllowSinglen()))) {
				groupNames = ApiCache.getApis();
			}
			return groupNames;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return null;
	}

	@GetMapping(value = "/v2/api-docs", produces = { MediaType.APPLICATION_JSON_VALUE, "application/hal+json" })
	@ResponseBody
	public ResponseEntity<Json> getDocumentation(@RequestParam(value = "group", required = false) String swaggerGroup,
			HttpServletRequest servletRequest) {

		String groupName = Optional.fromNullable(swaggerGroup).or(Docket.DEFAULT_GROUP_NAME);
		Documentation documentation = documentationCache.documentationByGroup(groupName);
		if (documentation == null) {
			logger.warn("Unable to find specification for group {}", groupName);
			return new ResponseEntity<Json>(HttpStatus.NOT_FOUND);
		}
		Swagger swagger = mapper.mapDocumentation(documentation);
		UriComponents uriComponents = componentsFrom(servletRequest, swagger.getBasePath());
		swagger.basePath(Strings.isNullOrEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath());
		if (isNullOrEmpty(swagger.getHost())) {
			swagger.host(hostName(uriComponents));
		}
		return new ResponseEntity<Json>(jsonSerializer.toJson(swagger), HttpStatus.OK);
	}

	private String hostName(UriComponents uriComponents) {
		if ("DEFAULT".equals(hostNameOverride)) {
			String host = uriComponents.getHost();
			int port = uriComponents.getPort();
			if (port > -1) {
				return String.format("%s:%d", host, port);
			}
			return host;
		}
		return hostNameOverride;
	}

}
